package com.yomahub.liteflow.script.java;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutor;
import com.yomahub.liteflow.script.exception.ScriptLoadException;

public class JavaExecutor  implements ScriptExecutor {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    private final Map<String, String> compiledScriptMap = new CopyOnWriteHashMap<>();

    @Override
    public ScriptExecutor init() {
        return this;
    }

    @Override
    public void load(String nodeId, String script) {
        try{
            MemoryClassLoader loader = MemoryClassLoader.genInstance();
            compiledScriptMap.put(nodeId,loader.registerJava(script));
            SpringContextUtil.registerSingleton(loader.getClass(compiledScriptMap.get(nodeId)));
        }catch (Exception e){
            String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage());
            throw new ScriptLoadException(errorMsg);
        }
    }

    @Override
    public Object execute(ScriptExecuteWrap wrap) throws Exception{
        List<String> errorList = new ArrayList<>();
        try{
            Object bean = SpringContextUtil.getBean(compiledScriptMap.get(wrap.getNodeId()));
            if (bean == null){
                String errorMsg = StrUtil.format("script for node[{}] is not loaded", wrap.getNodeId());
                throw new ScriptLoadException(errorMsg);
            }
            Method method = bean.getClass().getMethod("setSlotIndex",Integer.class);
            method.invoke(bean,wrap.getSlotIndex());
            method = bean.getClass().getMethod("process");
            Object result = method.invoke(bean);
            return result;
        }catch (Exception e){
            for (String scriptErrorMsg : errorList){
                log.error("\n{}", scriptErrorMsg);
            }
            throw e;
        }
    }

    @Override
    public void cleanCache() {
        for (String key:MemoryClassLoader.classBytes.keySet()) {
            if(SpringContextUtil.getBean(key) != null){
                SpringContextUtil.destroy(key);
            }
        }
        compiledScriptMap.clear();
        MemoryClassLoader.classBytes.clear();
    }
}
